﻿/*!
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

using System;
using System.IO;

namespace Pittypat
{
    /// <summary>
    /// 提供一个功能的参数列表、功能路径、当前用户信息、是否执行成功的指示以及错误消息等信息。
    /// </summary>
    /// <typeparam name="TArgs">功能的参数列表实体类类型，必须是一个从 <see cref="FunctionArgs"/> 继承而来的具有公共无参构造函数的非抽象类型。</typeparam>
    /// <remarks>
    /// FunctionModel 的对象实例由系统自动创建并传递给目标模块 Api 类中的某一个功能方法。在传递给目标方法前，参数列表、功能路径、用户信息都已经初始化。
    /// </remarks>
    public sealed class FunctionModel<TArgs> : Model
        where TArgs : FunctionArgs, new()
    {
        // 参数列表
        private TArgs args;

        // 功能路径
        private @string path;

        // 当前用户
        private IUser user;

        // 指示功能是否执行成功。
        private @bool success;

        // 执行后产生的消息。
        private @string message;

        // 环境变量
        private DynamicModel context;

        // 输出参数的列表
        private DynamicModel outArgs;

        /// <summary>
        /// 初始化 FunctionModel 类的新实例。
        /// </summary>
        public FunctionModel()
        {
            this.args = new TArgs();
            this.path = "";
            this.user = Pittypat.User.Anonymous;
            this.success = false;
            this.message = "";
            this.context = null;
            this.outArgs = null;
        }

        /// <summary>
        /// 获取功能的参数列表，其中包含了从客户端传来的各个参数值。
        /// </summary>
        public TArgs Args
        {
            get
            {
                return this.args;
            }
        }

        /// <summary>
        /// 获取所有输出参数的集合，包括方向为 Out 和 Inout 的两类参数。
        /// </summary>
        /// <remarks>
        /// 第一次访问该属性时内部创建一个 <see cref="DynamicModel"/> 对象实例并缓存，以后每次访问该属性都会自动刷新 <see cref="DynamicModel"/> 的属性列表。
        /// </remarks>
        public DynamicModel OutArgs
        {
            get
            {
                if (this.outArgs == null)
                {
                    this.outArgs = new DynamicModel();
                }

                // 刷新所有属性值
                foreach (var param in FunctionMeta.Find(typeof(TArgs)).Parameters)
                {
                    if (param.Direction == ParameterDirection.InOut || param.Direction == ParameterDirection.Out)
                    {
                        this.outArgs.SetProperty(param.Identifier, this.args[param.Identifier]);
                    }
                }

                return this.outArgs;
            }
        }

        /// <summary>
        /// 获取或者设置功能的路径，格式为 "{开发者代码}.{应用标识符}.{模块标识符}.{功能标识符}"。
        /// </summary>
        public @string Path
        {
            get
            {
                return this.path;
            }
            set
            {
                this.path = value;
            }
        }

        /// <summary>
        /// 获取当前用户的信息，如果用户尚未登录，则返回一个匿名用户的信息，而不是返回 null。
        /// </summary>
        public IUser User
        {
            get
            {
                return this.user;
            }
            internal set
            {
                this.user = value ?? Pittypat.User.Anonymous;
            }
        }

        /// <summary>
        /// 获取或者设置一个值，该值指示功能是否执行成功。
        /// </summary>
        /// <remarks>
        /// 如果将该属性设置为 false，则客户端将接收到错误消息，通过 <see cref="Message"/> 属性设置要显示给用户的错误信息。
        /// </remarks>
        public @bool Success
        {
            get
            {
                return this.success;
            }
            set
            {
                this.success = value;
            }
        }

        /// <summary>
        /// 获取或者设置执行后产生的消息。
        /// </summary>
        /// <remarks>
        /// 通常在将属性 <see cref="Success"/> 设置为 false 后，通过该属性向客户端发送详细的错误信息。
        /// </remarks>
        public @string Message
        {
            get
            {
                return this.message;
            }
            set
            {
                this.message = value;
            }
        }

        /// <summary>
        /// 获取或者设置当前功能的环境变量。
        /// </summary>
        /// <remarks>
        /// <para>环境变量将被转换为 JSON 字符串传递给目标数据服务，存储过程等可以接收到这些变量参数，并根据参数值执行不同的业务数据操作。</para>
        /// <para>目前环境变量提供两个额外的参数：{lang: string, ip: string}，分别表示客户端使用的首选语言和客户端的 IP 地址。</para>
        /// <para>SQL 代码编码人员可以通过数据库提供的 JSON 解析函数解析 JSON 字符串获得语言和 IP 地址，并根据语言和 IP 地址执行相应的操作，比如根据英语语言向用户返回英语消息。</para>
        /// <para>该属性是一个 <see cref="DynamicModel"/>，开发者可以在执行数据操作前添加自定义的属性。</para>
        /// </remarks>
        public DynamicModel Context
        {
            get
            {
                return this.context;
            }
            set
            {
                this.context = value;
            }
        }

        /// <summary>
        /// 将当前对象作为 JSON 字符串写入指定的文本流。
        /// </summary>
        /// <param name="writer">目标文本流。</param>
        /// <exception cref="ArgumentNullException">writer 是 null。</exception>
        public override void Write(TextWriter writer)
        {
            if (writer == null)
            {
                throw new ArgumentNullException("writer");
            }

            writer.Write('{');

            writer.Write("\"path\":");
            this.path.Write(writer);
            writer.Write(',');

            writer.Write("\"success\":");
            this.success.Write(writer);
            writer.Write(',');

            writer.Write("\"message\":");
            this.message.Write(writer);
            writer.Write(',');

            writer.Write("\"context\":");
            if (this.context == null)
            {
                writer.Write("null");
            }
            else
            {
                this.context.Write(writer);
            }
            writer.Write(',');

            writer.Write("\"args\":");
            this.args.Write(writer);

            writer.Write('}');
        }
    }
}
